﻿using AzukiMap.Map;
using Geometries = GisSharpBlog.NetTopologySuite.Geometries;
using System;
using GeoAPI.Geometries;
using System.Collections.Generic;

namespace AzukiMap.Tools
{
    public class GeometryUtil : Geometries.GeometryFactory
    {
        public static GeometryUtil Default = new GeometryUtil();
        public static Dictionary<int, GeometryUtil> utils = new Dictionary<int, GeometryUtil>();

        public static GeometryUtil GetBySRID(int srid) {
            if (utils.ContainsKey(srid)) {
                return utils[srid];
            }
            GeometryUtil util = new GeometryUtil(defaultPM, srid);
            utils[srid] = util;
            return util;
        }

        private static Geometries.PrecisionModel defaultPM = new Geometries.PrecisionModel();

        public GeometryUtil()
            : base()
        {
        }

        private GeometryUtil(Geometries.PrecisionModel precisionModel, int SRID)
            : base(precisionModel, SRID)
        {
        }

        public IPoint CreatePoint(double x, double y)
        {
            ICoordinate coord = new Geometries.Coordinate(x, y);
            IPoint p = this.CreatePoint(coord);
            return p;
        }

        /// <summary>
        /// Create a polygon from four points.
        /// The polygon is discribed as a counterclockwise order.
        /// </summary>
        /// <param name="tlX">top left x</param>
        /// <param name="tlY">top left y</param>
        /// <param name="blX">bottom left x</param>
        /// <param name="blY">bottom left y</param>
        /// <param name="brX">bottom right x</param>
        /// <param name="brY">bottom right y</param>
        /// <param name="trX">top right x</param>
        /// <param name="trY">top right y</param>
        /// <returns>extent polygon (counterclockwise)</returns>
        public IPolygon CreateExtentPolygon(
            double tlX, double tlY, double blX, double blY,
            double brX, double brY, double trX, double trY)
        {
            ICoordinate[] coords = new Geometries.Coordinate[5];
            coords[0] = new Geometries.Coordinate(tlX, tlY);
            coords[1] = new Geometries.Coordinate(blX, blY);
            coords[2] = new Geometries.Coordinate(brX, brY);
            coords[3] = new Geometries.Coordinate(trX, trY);
            coords[4] = new Geometries.Coordinate(tlX, tlY);
            ILinearRing shell = this.CreateLinearRing(coords);
            IPolygon extent = this.CreatePolygon(shell, null);
            return extent;
        }

        /// <summary>
        /// Create a extent polygon from two points.
        /// The extent polygon is a counterclockwise order.
        /// </summary>
        /// <param name="minX"></param>
        /// <param name="minY"></param>
        /// <param name="maxX"></param>
        /// <param name="maxY"></param>
        /// <returns>extent polygon (counterclockwise)</returns>
        public IPolygon CreateExtentPolygon(
            double minX, double minY, double maxX, double maxY)
        {
            ICoordinate[] coords = new Geometries.Coordinate[5];
            coords[0] = new Geometries.Coordinate(minX, maxY);
            coords[1] = new Geometries.Coordinate(minX, minY);
            coords[2] = new Geometries.Coordinate(maxX, minY);
            coords[3] = new Geometries.Coordinate(maxX, maxY);
            coords[4] = new Geometries.Coordinate(minX, maxY);
            ILinearRing shell = CreateLinearRing(coords);
            IPolygon extent = CreatePolygon(shell,null);
            return extent;
        }

        public double GetExtentPolygonWidth(IPolygon extentPoly)
        {
            ICoordinate tl = GetExtentPoint(extentPoly, ExtentOrder.TOP_LEFT);
            ICoordinate tr = GetExtentPoint(extentPoly, ExtentOrder.TOP_RIGHT);
            double tlX = tl.X;
            double tlY = tl.Y;
            double trX = tr.X;
            double trY = tr.Y;
            double width = Math.Sqrt(Math.Pow((trX - tlX), 2) + Math.Pow((trY - tlY), 2));
            return width;
        }

        public double GetExtentPolygonHeight(IPolygon extentPoly)
        {
            ICoordinate tl = GetExtentPoint(extentPoly, ExtentOrder.TOP_LEFT);
            ICoordinate bl = GetExtentPoint(extentPoly, ExtentOrder.BOTTOM_LEFT);
            double tlX = tl.X;
            double tlY = tl.Y;
            double trX = bl.X;
            double trY = bl.Y;
            double height = Math.Sqrt(Math.Pow((trX - tlX), 2) + Math.Pow((trY - tlY), 2));
            return height;
        }

        public double GetPolygonEnvelopeWidth(IPolygon poly)
        {
            IEnvelope envelope = poly.EnvelopeInternal;
            return envelope.Width;
        }

        /// <summary>
        /// Return a selected position of point from envelope polygon.
        /// </summary>
        /// <param name="extentPoly"></param>
        /// <param name="order"></param>
        /// <returns></returns>
        public ICoordinate GetExtentPoint(IPolygon extentPoly, ExtentOrder order)
        {
            return extentPoly.Coordinates[(int)order];
        }

        public double[] GetExtentPointsArr(IPolygon extentPoly)
        {
            double[] points = new double[8];
            ICoordinate tl = GetExtentPoint(extentPoly, ExtentOrder.TOP_LEFT);
            ICoordinate bl = GetExtentPoint(extentPoly, ExtentOrder.BOTTOM_LEFT);
            ICoordinate br = GetExtentPoint(extentPoly, ExtentOrder.BOTTOM_RIGHT);
            ICoordinate tr = GetExtentPoint(extentPoly, ExtentOrder.TOP_RIGHT);
            points[(int)ExtentArrOrder.TOP_LEFT_X] = tl.X;
            points[(int)ExtentArrOrder.TOP_LEFT_Y] = tl.Y;
            points[(int)ExtentArrOrder.BOTTOM_LEFT_X] = bl.X;
            points[(int)ExtentArrOrder.BOTTOM_LEFT_Y] = bl.Y;
            points[(int)ExtentArrOrder.BOTTOM_RIGHT_X] = br.X;
            points[(int)ExtentArrOrder.BOTTOM_RIGHT_Y] = br.Y;
            points[(int)ExtentArrOrder.TOP_RIGHT_X] = tr.X;
            points[(int)ExtentArrOrder.TOP_RIGHT_Y] = tr.Y;

            return points;
        }

        public IPoint GetPolygonCenter(IPolygon extentPolygon)
        {
            ICoordinate[] coords = extentPolygon.Coordinates;
            double sumX = 0;
            double sumY = 0;
            double centerX = 0;
            double centerY = 0;

            // get sum of x,y. the last point is the same as first point.
            for (int i = 0; i < coords.Length - 1; i++)
            {
                sumX += coords[i].X;
                sumY += coords[i].Y;
            }
            if (sumX != 0)
            {
                centerX = sumX / (coords.Length - 1);
            }
            if (sumY != 0)
            {
                centerY = sumY / (coords.Length - 1);
            }
            return CreatePoint(centerX, centerY);
        }

        public IPolygon EnvelopeToPolygon(IEnvelope env)
        {
            IPolygon poly;
            if (env == null)
            {
                poly = null;
            }
            else
            {
                poly = CreateExtentPolygon(env.MinX, env.MinY, env.MaxX, env.MaxY);
            }
            return poly;
        }


    }
}
